package cn.workreport.interceptor;

import cn.hutool.core.util.ObjectUtil;
import cn.workreport.config.conf.TokenConfiguration;
import cn.workreport.modules.common.annotations.PassToken;
import cn.workreport.modules.common.annotations.UserLoginToken;
import cn.workreport.modules.common.entity.TokenParseEntity;
import cn.workreport.modules.common.entity.UserCacheEntity;
import cn.workreport.modules.common.enums.IsEnum;
import cn.workreport.modules.common.exception.HasNoPermissionException;
import cn.workreport.modules.common.exception.TokenAuthExpiredException;
import cn.workreport.modules.permission.enums.PermissionEnum;
import cn.workreport.modules.role.entity.Role;
import cn.workreport.modules.users.entity.UserEntity;
import cn.workreport.util.ThreadLocalUtil;
import cn.workreport.util.TokenUtil;
import cn.workreport.util.UserChacheFromToken;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

@Log4j2
@Component
public class AuthHandlerInterceptor implements HandlerInterceptor {

    @Autowired
    TokenUtil tokenUtil;

    @Autowired
    TokenConfiguration tokenConfiguration;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.err.println("======== 进入拦截器 ========");
        String token = request.getHeader("login-token");
        System.out.println("\t请求的 token ===>" + token);
        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        Class clazz = handlerMethod.getBeanType();
        // 1. 检查请求的【方法】中是否有 passtoken 注解，有则直接跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        // 2. 检查请求的【方法】或者【类】中有没有需要用户权限 UserLoginToken 的注解
        if (method.isAnnotationPresent(UserLoginToken.class) || clazz.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken;
            if (method.isAnnotationPresent(UserLoginToken.class)) {
                userLoginToken = method.getAnnotation(UserLoginToken.class);
            } else {
                userLoginToken = (UserLoginToken) clazz.getAnnotation(UserLoginToken.class);
            }
            if (userLoginToken.required()) {
                // token 为空 不予通过
                if (null == token || "".equals(token.trim())) {
                    throw new TokenAuthExpiredException("需要登录才能访问，请先登录");
                }
                // 先解析 token
                TokenParseEntity tokenParseEntity = null;
                try {
                    tokenParseEntity = tokenUtil.parseToken(token);
                } catch (Exception e) {
                    throw new TokenAuthExpiredException("登录凭证信息有误，请重新登录");
                }
                Integer userId = tokenParseEntity.getUserId();
                // 检查 token 是否存在

                int checkTokenRes = tokenUtil.checkToken(token, userId);
                if (0 == checkTokenRes) {
                    throw new TokenAuthExpiredException("登录信息已失效");
                } else if (2 == checkTokenRes) {
                    throw new TokenAuthExpiredException("您的账号已在其他地方登录，如果不是本人操作，建议修改密码");
                }
//                Long tokenExpireTime = tokenUtil.getExpireTime(token);
                // 1.判断 token 是否过期
                long resultTime = System.currentTimeMillis() - tokenParseEntity.getStartTime();
                // 年轻 token
                if (resultTime <= tokenConfiguration.getYangToken()) {
                    System.out.println("\t年轻 token 不需要刷新");
                }
                // 即将过期的 token, 需要刷新
                else if (resultTime <= tokenConfiguration.getOldToken()) {
                    System.out.println("\t老年 token 需要刷新");
                    // 拿到新的 token
                    token = tokenUtil.refreshToken(userId);
                    response.addHeader("login-token", token);
                }
                // 过期 token 就返回 token 无效
                else {
                    throw new TokenAuthExpiredException("登录已过期，请重新登录！");
                }
                // 根据 token 中的 userId 获取用户信息
                UserCacheEntity userCacheEntity = tokenUtil.getUserCache(userId);
                UserEntity user = userCacheEntity.getUser();
                // 拦截不存在或已被停用的用户
                if (ObjectUtil.isEmpty(user) || IsEnum.YES.equals(user.getDeleted())) {
                    throw new TokenAuthExpiredException("用户不存在，请重新登录");
                }
                // 把 用户信息 存在当前线程的缓存中
                UserChacheFromToken.setUserId(userId);
                // 超级管理员跳过权限验证
                if (!ObjectUtil.isEmpty(user.getIsSupperAdmin()) && user.getIsSupperAdmin()) {
                    log.info("超级管理员跳过权限验证");
                    return true;
                }

                // 2.角色匹配
                PermissionEnum[] needPermissionList = userLoginToken.permission();
                System.err.println("\t当前接口需要的权限 ====>" + Arrays.toString(needPermissionList));

                // 接口需要权限
                if (needPermissionList.length > 0) {
                    // 因为角色权限是跟机构绑定，如果没有绑定机构，则优先提示机构未绑定
                    if (ObjectUtil.isEmpty(user.getOrgId())) {
                        throw new HasNoPermissionException("当前用户未关联机构，请先关联");
                    }
                    if (ObjectUtil.isEmpty(user.getRoleId())) {
                        throw new HasNoPermissionException("当前用户未关联角色，请联系管理员");
                    }
                    Role userRole = userCacheEntity.getUserRole();
                    if (ObjectUtil.isEmpty(userRole)) {
                        throw new HasNoPermissionException("当前用户关联角色不存在，请联系管理员");
                    }
                    List<String> userPermissionList = userRole.getPermissions();
                    log.info("当前用户权限列表 ===>" + userPermissionList);
                    for (PermissionEnum needPermission : needPermissionList) {
                        for (String userPermission : userPermissionList) {
                            if (needPermission.getValue().equals(userPermission)) {
                                return true;
                            }
                        }
                    }
                    throw new HasNoPermissionException("抱歉，当前用户没有权限，请联系管理员");
                }

                return true;
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 执行结束后释放 ThreadLocal 资源防止oom(资源溢出)
        ThreadLocalUtil.remove();
    }
}
